home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Utilities
/
Workspace
/
MonsterShelf
/
Source
/
ShelfView.m
< prev
Wrap
Text File
|
1995-06-12
|
16KB
|
728 lines
#import "ShelfView.h"
#import "IconView.h"
#import "IconDragView.h"
#import "compositeBackground.h"
#import <appkit/appkit.h>
#import <ansi/stdio.h>
#import <ansi/string.h>
#define MONSTERSHELF_FILE ".MonsterShelf"
#define GRID_ENABLE "GridEnabled"
#define GRID_VALUE "GridValue"
#define MAX_GRID_VALUE 1024
#define MIN_GRID_VALUE 32
#define DEFAULT_GRID_STRING "84"
#define DEFAULT_GRID_VALUE 84
#define round(x,y) (((x)+(y-1))/(y)*(y))
@implementation ShelfView
+ initialize
{
static NXDefaultsVector defaults = {
{GRID_ENABLE, "NO"},
{GRID_VALUE, DEFAULT_GRID_STRING},
{NULL}
};
NXRegisterDefaults([NXApp appName], defaults);
return self;
}
- initFrame:(const NXRect *) aFrame
{
const char *colorString;
const char *const types[1] = {NXFilenamePboardType};
int screenCount;
char *backgroundString;
NXScreen *screens;
unsigned int i;
for (i=0; i<NUM_MOUNT_SLOTS; i++)
mountSlots[i] = nil;
[super initFrame:aFrame];
[[self window] setDelegate:self];
[self registerForDraggedTypes:types count:1];
/*
* Determine the background color.
*/
useBGColor = NO;
[NXApp getScreens:&screens count:&screenCount];
if (screens[0].depth == NX_TwoBitGrayDepth)
backgroundString = "BWBackgroundColor";
else
backgroundString = "BackgroundColor";
colorString = NXGetDefaultValue("NeXT1", backgroundString);
if (colorString) {
float r, g, b;
sscanf(colorString, "%f %f %f", &r, &g, &b );
bgColor = NXConvertRGBAToColor(r, g, b, NX_NOALPHA);
useBGColor = YES;
}
/*
* Now that we've set up our view's appearance, do the rest of the
* initialization we need to do.
*/
[self readShelf];
return self;
}
- free
{
return [super free];
}
- (BOOL) acceptsFirstMouse
{
return YES;
}
- (NXColor) backgroundColor
{
if (useBGColor)
return bgColor;
else
return NX_COLORLTGRAY;
}
- (BOOL) isAnyViewAt:(NXPoint) aPoint besides:aView
{
unsigned int i;
unsigned int max = [[self subviews] count];
for (i = 0; i < max; i++) {
int x = aPoint.x;
int y = aPoint.y;
NXRect rect;
if ([[self subviews] objectAt:i] == aView)
continue;
[[[self subviews] objectAt:i] getFrame:&rect];
if ((int) rect.origin.x == x && (int) rect.origin.y == y)
return YES;
}
return NO;
}
/*
* Align all of our IconViews on the grid. Take care so that none overlap.
*/
- (void) alignSubviews
{
unsigned int i;
unsigned int max = [[self subviews] count];
unsigned int grid = [self gridValue];
if (![self gridEnabled])
return;
for (i = 0; i < max; i++) {
id view = [[self subviews] objectAt:i];
NXRect rect;
NXPoint candidatePt;
int count;
if (![view isKindOf:[IconView class]])
continue;
/*
* Make the icon the right size, and then compute the new origin.
*/
[view getFrame:&rect];
candidatePt.x = round((int) rect.origin.x, grid);
candidatePt.y = round((int) rect.origin.y, grid);
count = bounds.size.height / grid * bounds.size.width / grid;
while (count-- > 0 && [self isAnyViewAt:candidatePt besides:view]) {
candidatePt.x += grid;
if (candidatePt.x + rect.size.width > bounds.size.width) {
candidatePt.x = 0;
candidatePt.y += grid;
if (candidatePt.y + rect.size.height > bounds.size.height)
candidatePt.y = grid;
}
}
[view sizeTo:grid :grid];
[view moveTo:candidatePt.x :candidatePt.y];
}
[self display];
}
- (BOOL) gridEnabled
{
const char *enabled = NXGetDefaultValue([NXApp appName], GRID_ENABLE);
return (enabled && !strcmp(enabled, "YES"));
}
- (void) setGridEnabled:(BOOL) flag
{
(void) NXWriteDefault([NXApp appName], GRID_ENABLE, flag ? "YES" : "NO");
[window disableDisplay];
[[self subviews] freeObjects];
[IconView resetCachedImages];
[self readShelf];
[[window reenableDisplay] display];
}
- (unsigned int) gridValue
{
const char *gridValue = NXGetDefaultValue([NXApp appName], GRID_VALUE);
if (gridValue)
return atoi(gridValue);
else
return DEFAULT_GRID_VALUE;
}
- setGridValue:(unsigned int) gridValue
{
char gridString[20];
if (gridValue == [self gridValue])
return self;
if (gridValue < MIN_GRID_VALUE)
gridValue = MIN_GRID_VALUE;
else if (gridValue > MAX_GRID_VALUE)
gridValue = MAX_GRID_VALUE;
sprintf(gridString, "%d", gridValue);
(void) NXWriteDefault([NXApp appName], GRID_VALUE, gridString);
return self;
}
- drawSelf:(const NXRect *) rects :(int) rectCount
{
if (useBGColor) {
NXSetColor(bgColor);
NXRectFill(rects);
}
else
compositeFromWorkspaceWindow(rects->origin.x, rects->origin.y,
rects->size.width, rects->size.height);
return self;
}
- deselectAll:sender
{
[subviews makeObjectsPerform:@selector(setState:) with:(id) 0];
return NO;
}
- removeView:aView
{
NXRect viewFrame;
[aView getFrame:&viewFrame];
[aView removeFromSuperview];
[self display:&viewFrame :1 :NO];
return self;
}
- addView:aView
{
NXRect viewFrame;
[aView getFrame:&viewFrame];
[self addSubview:aView];
[self display:&viewFrame :1 :NO];
return self;
}
- deleteView:aView
{
[self removeView:aView];
[aView free];
[self writeShelf];
return self;
}
/*
* Return true if the point is in the area we use to get rid of views
*/
- (BOOL) isDeadZone:(NXPoint *) aPoint
{
NXRect goodZone = bounds;
NXInsetRect(&goodZone, 2, 2);
return !NXMouseInRect(aPoint, &goodZone, NO);
}
- (void) createViewForPath:(const char *) path at:(NXPoint *) point
{
id image = [[Application workspace] getIconForFile:path];
id newView = [IconDragView allocFromZone:[self zone]];
NXCoord grid = [self gridValue];
struct stat st;
NXRect aRect;
unsigned int i = 0;
NXPoint viewOrigin;
if (stat(path, &st) < 0)
return;
if (!point) {
/*
* If the caller didn't know where to put the view, stick it one of
* our default slots.
*/
while (i < NUM_MOUNT_SLOTS && mountSlots[i])
++i;
if (i < NUM_MOUNT_SLOTS)
mountSlots[i] = newView;
viewOrigin.x = i * [self gridValue];
viewOrigin.y = [self gridValue];
aRect.origin = viewOrigin;
}
else
aRect.origin = *point;
/*
* If the grid is on, make sure the size of the view we're about
* to create is pegged to the grid size.
*/
if ([self gridEnabled]) {
aRect.size.width = grid;
aRect.size.height = grid;
}
[newView initFrame:&aRect image:image data:path andLength:strlen(path)+1
useSize:[self gridEnabled]];
[self addSubview:newView];
[newView getFrame:&aRect];
[self display:&aRect :1 :NO];
}
static BOOL
prefix(const char *prefix, const char *string)
{
while (*prefix && *string && *prefix == *string) {
prefix++;
string++;
}
return *prefix == '\0';
}
- (void) removeViewForPath:(const char *) fullPath
{
int i = [[self subviews] count];
while (i > 0) {
char *path;
unsigned int len;
id view = [[self subviews] objectAt:i];
if ([view isKindOf:[IconView class]]) {
[view getData:(void *) &path andLength:&len];
if (prefix(fullPath, path)) {
unsigned int j=0;
while (j < NUM_MOUNT_SLOTS && mountSlots[j] != view)
++j;
if (j < NUM_MOUNT_SLOTS)
mountSlots[j] = nil;
[self deleteView:view];
}
}
i--;
}
}
/*
* Find the right position for the new image, based on the grid and the
* mouse's location.
*/
- (NXPoint) viewLocationForContext:(id <NXDraggingInfo>)dragContext
{
NXPoint newLoc;
unsigned int grid = [self gridValue];
NXPoint imagePt = [dragContext draggedImageLocation];
NXPoint mousePt = [dragContext draggingLocation];
NXPoint imageOffset;
[draggedView getImagePoint:&imageOffset andHilitePoint:NULL];
if ([self gridEnabled]) {
NXRect rect;
[draggedView getFrame:&rect];
newLoc.x = mousePt.x - ((int) mousePt.x % grid) +
(grid - rect.size.width) / 2;
newLoc.y = mousePt.y - (int) mousePt.y % grid;
}
else {
newLoc.x = imagePt.x - imageOffset.x;
newLoc.y = imagePt.y - imageOffset.y;
}
return newLoc;
}
/*
* Make a ghost image to indicate that we're really a destination.
*/
- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{
NXPoint newLoc;
NXSize aSize, *sizePtr = NULL;
if ([self gridEnabled]) {
aSize.width = [self gridValue];
aSize.height = aSize.width;
sizePtr = &aSize;
}
draggedView = [[IconView allocFromZone:[self zone]]
initFromDragContext:sender andSize:sizePtr];
if ([self gridEnabled]) {
newLoc = [self viewLocationForContext:sender];
[draggedView moveTo:newLoc.x :newLoc.y];
[draggedView setGhost:YES];
[self addView:draggedView];
}
return NX_DragOperationAll;
}
/*
* Move the dragged image, but only do it if we need to (that is, if the
* mouse moved to a new grid cell).
*/
- (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
{
NXPoint mouseLoc;
NXPoint newLoc = [self viewLocationForContext:sender];
NXRect aFrame;
if (![self gridEnabled])
return NX_DragOperationAll;
/*
* If the icon was dragged off the edge, hide it somewhere!
*/
mouseLoc = [sender draggingLocation];
if ([self isDeadZone:&mouseLoc]) {
newLoc.x = -100;
newLoc.y = -100;
}
[draggedView getFrame:&aFrame];
if (aFrame.origin.x != newLoc.x || aFrame.origin.y != newLoc.y) {
[draggedView moveTo:newLoc.x :newLoc.y];
[self display:&aFrame :1 :NO]; /* erase old */
aFrame.origin = newLoc;
[self display:&aFrame :1 :NO]; /* draw new */
}
return NX_DragOperationAll;
}
/*
* Get rid of the resources we used to drag the image around.
*/
- draggingExited:(id <NXDraggingInfo>)sender
{
[self removeView:draggedView];
[draggedView free];
return self;
}
/*
* Eat the result...
*/
- (BOOL) prepareForDragOperation:sender
{
NXPoint mouseLoc;
NXRect aFrame;
NXPoint newLoc = [self viewLocationForContext:sender];
/*
* If the dragged item landed in the dead zone, get rid of it. If
* the dragged item originated with us, we "accept" the image to tell
* the source to free it.
*/
mouseLoc = [sender draggingLocation];
if ([self isDeadZone:&mouseLoc]) {
[draggedView getFrame:&aFrame];
[draggedView free];
[self display:&aFrame :1 :NO];
return [sender isDraggingSourceLocal];
}
/*
* Turn the dragged IconView into an IconDragView that's actually
* capable of being a drag destination, too.
*/
[draggedView moveTo:newLoc.x :newLoc.y];
[self addView:[IconDragView copyIconView:draggedView]];
[draggedView free];
return YES;
}
- (BOOL) performDragOperation:sender
{
return YES;
}
/*
* Actually write the stuff way down here. It's completely at the end
* of the operation, so a slow write won't hose the UI.
*/
- concludeDragOperation:(id <NXDraggingInfo>)sender
{
[self writeShelf];
return self;
}
/*
* Be a drag source, too
*/
- setDragView:aView onEvent:(NXEvent *) e withOffset:(NXPoint *) offset atLocation:(const NXPoint *) location
{
id pb = [Pasteboard newName:NXDragPboard];
void *data;
unsigned int length;
NXPoint myLoc;
/*
* Initiate a drag operation. Copy stuff into the pasteboard,
* then start dragging. To simplify matters elsewhere, we try
* to make a local dragging operation look just like a non-local
* one.
*/
dragSourceView = aView;
keepSourceOnShelf = (e->flags & NX_ALTERNATEMASK) ? YES : NO;
[aView getData:&data andLength:&length];
[pb declareTypes:&NXFilenamePboardType num:1 owner:nil];
[pb writeType:NXFilenamePboardType data:data length:length];
myLoc = *location;
[aView convertPoint:&myLoc toView:self];
[self dragImage:[aView image] at:&myLoc
offset:offset event:e pasteboard:pb
source:self slideBack:YES];
return self;
}
- draggedImage:(NXImage *)image beganAt:(NXPoint *)screenPoint
{
NXRect theFrame;
[dragSourceView getFrame:&theFrame];
[self display:&theFrame :1 :NO];
if (!keepSourceOnShelf)
[self removeView:dragSourceView];
return self;
}
/*
* A drag operation, with us as the source, finished. If it was an
* unsuccessful drag then, put the source back! If it was a successful
* drag, and we weren't the destination, then remove the thing from the
* shelf.
*/
- draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint
deposited:(BOOL)didDeposit
{
char *path;
unsigned int len;
struct stat st;
/*
* Check to see if we should keep the source dir on the shelf. We
* do this if the keepSourceOnShelf flag is set, and if the file
* under the icon still exists.
*/
[dragSourceView getData:(void **) &path andLength:&len];
if (keepSourceOnShelf && path && stat(path, &st) == 0) {
keepSourceOnShelf = NO;
return self;
}
/*
* The source isn't on the screen, so either get rid of the source, or
* put it back.
*/
if (didDeposit)
[self deleteView:dragSourceView];
else
[self addView:dragSourceView];
return self;
}
- (NXDragOperation) draggingSourceOperationMaskForLocal:(BOOL)flag
{
return NX_DragOperationAll;
}
/*
* Open the shelf file.
*/
- (FILE *) openShelfFor:(char *) how
{
char path[MAXPATHLEN];
sprintf(path, "%s/%s", NXHomeDirectory(), MONSTERSHELF_FILE);
return fopen(path, how);
}
/*
* Close it.
*/
- closeShelf:(FILE *) file
{
fclose(file);
return self;
}
/*
* Read the contents of the shelf in from a file. The file's format consists
* of lines of the form:
*
* x y path
*
* where the two numbers x,y specify the origin of the particular view on the
* shelf, and path specifies the path to the workspace. Somewhat bogusly,
* we assume the path starts at character 14.
*/
- readShelf
{
FILE *file;
char line[MAXPATHLEN + 30];
char *path;
NXPoint point;
file = [self openShelfFor:"r"];
if (file == NULL)
return self;
while (fgets(line, sizeof(line), file)) {
/*
* Parse the line in the shelf. It's too bad that we can't use
* sscanf to parse the whole line!
*/
sscanf(line, "%f %f", &point.x, &point.y);
/* file string starts after second number, char 14 */
if (strlen(line) > 14) {
path = line + 14;
if (rindex(path, '\n') != NULL)
*rindex(path, '\n') = '\0';
}
else
continue;
/*
* Make a spot for this guy...
*/
[self createViewForPath:path at:&point];
}
[self closeShelf:file];
if ([self gridEnabled])
[self alignSubviews];
return self;
}
/*
* Write the contents of the shelf out to the shelf file.
*/
- writeShelf
{
FILE *file;
NXRect rect;
int i;
file = [self openShelfFor:"w"];
if (file == NULL)
return self;
for (i = 0; i < [[self subviews] count]; i++) {
id view = [[self subviews] objectAt:i];
unsigned int length;
char *path;
if (![view isKindOf:[IconView class]] || [view isOnRemovableMedia])
continue;
[view getData:(void **) &path andLength:&length];
[view getFrame:&rect];
fprintf(file, "%6.0f %6.0f %s\n", rect.origin.x, rect.origin.y, path);
}
[self closeShelf:file];
return self;
}
@end